﻿using System;
using System.ComponentModel;
using System.Net;
using System.Net.Mail;
using MassMailing.WPF.Content;
using MassMailing.WPF.Settings;

namespace MassMailing.WPF.Commands
{
    /// <summary>
    /// A command that performs the mailing of a single mail message.
    /// </summary>
    public class SendMailCommand : BaseCommand
    {
        #region Constants

        private const int MAX_RETRIES = 3;

        #endregion

        #region Fields

        public event AsyncCompletedEventHandler MessageSent;
        public event SendCompletedEventHandler SendCompleted;

        private SMTPSetting _smtpSetting;
        private Recipient _recipient;
        private MassMailingMessage _message;

        #endregion

        #region Constructors

        /// <summary>
        /// Default Constructor
        /// </summary>
        /// <param name="smtpSetting">The smtp setting to use when sending the message.</param>
        /// <param name="recipient">The recipient of the message.</param>
        /// <param name="message">The message to be sent in this command.</param>
        public SendMailCommand(SMTPSetting smtpSetting, Recipient recipient, MassMailingMessage message)
        {
            _smtpSetting = smtpSetting;
            _recipient = recipient;
            _message = message;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Creates an smtp client based on the smtp settings.
        /// </summary>
        /// <param name="smtpSetting">The smtp settings to use in order to create a client.</param>
        /// <returns>The smtp client created based on the smtp settings.</returns>
        private SmtpClient CreateSmtpClient(SMTPSetting smtpSetting)
        {
            SmtpClient client = smtpSetting.Port.HasValue
                                ? new SmtpClient(smtpSetting.SMTPServer, smtpSetting.Port.Value)
                                : new SmtpClient(smtpSetting.SMTPServer);
            client.EnableSsl = smtpSetting.SSLEnabled ?? false;
            client.Timeout = 10000;
            client.DeliveryMethod = SmtpDeliveryMethod.Network;
            client.UseDefaultCredentials = false;
            client.Credentials = new NetworkCredential(smtpSetting.EmailAddress, smtpSetting.Password);

            return client;
        }

        /// <summary>
        /// The method that performs the send mail command.
        /// </summary>
        protected override void PerformCommand()
        {
            bool successful = false;
            int retry = 0;

            while (!successful)
            {
                try
                {
                    using (SmtpClient client = CreateSmtpClient(_smtpSetting))
                    using (MailMessage mm = new MailMessage())
                    {
                        mm.To.Add(_recipient.ToString());
                        if (!string.IsNullOrWhiteSpace(_message.From))
                        {
                            mm.From = new MailAddress(_message.From);
                        }
                        mm.Subject = _message.Subject.Replace("[name]", _recipient.Name).Replace("[email]", _recipient.EmailAddress);
                        mm.Body = _message.Body.Replace("[name]", _recipient.Name).Replace("[email]", _recipient.EmailAddress);
                        mm.IsBodyHtml = true;

                        foreach (string attachmentFileName in _message.Attachments)
                        {
                            mm.Attachments.Add(new Attachment(attachmentFileName));
                        }

                        // Only raise the event on the first try.
                        if (retry == 0) RaiseMessageSent(_recipient);
                        client.Send(mm);
                        mm.Dispose();
                        client.Dispose();
                        AsyncCompletedEventArgs e = new AsyncCompletedEventArgs(null, false, _recipient);
                        RaiseSendCompleted(this, e);
                        successful = true;
                    }
                }
                catch (Exception ex)
                {
                    retry++;
                    if (retry >= MAX_RETRIES)
                    {
                        AsyncCompletedEventArgs e = new AsyncCompletedEventArgs(ex, false, _recipient);
                        RaiseSendCompleted(this, e);
                        break;
                    }
                } 
            }
        }

        /// <summary>
        /// Raises the message sent event for the recipient specified.
        /// </summary>
        /// <param name="recipient">The recipient to raise the message sent for.</param>
        private void RaiseMessageSent(Recipient recipient)
        {
            if (MessageSent != null)
            {
                AsyncCompletedEventArgs e = new AsyncCompletedEventArgs(null, false, recipient);

                MessageSent(this, e);
            }
        }

        /// <summary>
        /// Raises the send completed event.
        /// </summary>
        /// <param name="sender">The sender that created the event.</param>
        /// <param name="e">The event args that are associated with this event.</param>
        private void RaiseSendCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (SendCompleted != null)
            {
                SendCompleted(sender, e);
            }
        }

        #endregion
    }
}
